/*
 * jQuery Svg Routes Plugin 0.0.1
 *
 * Copyright 2013, iQiyi
 *
 */

/*Dependence: Raphael.js, */

// the widget definition, where "custom" is the namespace,
$.widget( "custom.svgroutes", {
    options: {
        data: [],
        fill: 'rgb(255,190,135)',
        rectWidth: 80,
        rectHeight: 40,
        spaceWidth: 40,
        spaceHeight: 20,
        fontSize: 12,
        fontFamily: "Simsun",
        r: 4,
        dragSupport: false,
        workflowStatus: 'Underway',
        workflowId: '',
        mousehover: function () {}
    },

    // the constructor
    _create: function () {
        // 各种全局变量的保存
        this.startX = 0;
        this.currentLine = [];
        this.nextLine = [];
        this.direction = 1;
        this.startY = 0;
        this.matrix = [];
        this.spaceWidth = this.options.spaceWidth;
        this.spaceHeight = this.options.spaceHeight;

        // 初始化时扩展一次即可
        this._extendRaphael();
    },

    _init: function() {
        var position, startLine;
        var $tip = this.options.$tip;
        var self = this;


        this.matrix = this.options.data;
        this.matrix.forEach(function (line) { // 写默认的item的宽高
            line.forEach(function (metaItem) {
                metaItem.rectWidth = metaItem.rectWidth || self.options.rectWidth;
                metaItem.rectHeight = metaItem.rectHeight || self.options.rectHeight;
            });
        });

        // 组装后台数据为二维矩阵
        size = this._calculatePosition();
        if(self.options.isBodyOnly){
            size.h += 2 * this.options.rectHeight; // 绘制提示框的需要
            size.w += 1 * self.spaceWidth;
        }else{
            size.h += 4 * this.options.rectHeight; // 绘制提示框的需要
            size.w += 2 * self.spaceWidth;
        }
        this._drawMatrix(size);
        this._bindTipEvents($tip);
    },
    _getLineWidth: function (line) {
        var r = 0;
        line.forEach(function (item) {r = r > item.rectWidth ? r : item.rectWidth; });
        return r;
    },
    _calculatePosition: function () {
        var maxLineHeight = 0, maxHeightLineIndex = 0, matrixWidth = 0, tempStartY = 0, tempStartX = 0, matrix = this.matrix, subMatrix = [], self = this;
        var getLineHeight = function (line) {
            var result = 0;
            line.forEach(function (item) { result += item.rectHeight + self.spaceHeight; });
            return result;
        };
        var getPosX = function (leftItem, rightItem, leftLineWidth, nextLineWidth) {
            return leftItem.position.x + (leftLineWidth - leftItem.rectWidth) / 2 + leftItem.rectWidth
                + self.spaceWidth + (nextLineWidth - rightItem.rectWidth) / 2;
        };
        matrix.forEach(function (line, index) {
            var lineHeight = getLineHeight(line);
            if (lineHeight > maxLineHeight) {
                maxLineHeight = lineHeight;
                maxHeightLineIndex = index;
            }
        });
        console.log('Max height line index is: ' + maxHeightLineIndex);
        matrix[0][0]['position'] = {
            x: self.spaceWidth,
            y: (maxLineHeight - matrix[0][0]['rectHeight']) / 2 + self.spaceHeight
        };

        // 先布局最高列
        matrixWidth += self._getLineWidth(matrix[maxHeightLineIndex]) + self.spaceWidth;
        matrix[maxHeightLineIndex].forEach(function (metaItem, index) {
            var x = (metaItem.rectWidth + self.spaceWidth) * maxHeightLineIndex;
// console.log('X with index: ' + index + ' here is: ' + x);
            metaItem.position = {
                x: x,
                y: tempStartY
            };
            tempStartY = tempStartY + self.spaceHeight + metaItem.rectHeight;
        });

        // 再左向计算最高列左侧的matrix的位置
        subMatrix = matrix.slice(0, maxHeightLineIndex);
        subMatrix.forEach(function (line, index) {
            var lineWidth = self._getLineWidth(line);
            // 由右向左计算，右侧的列的位置是已知的，左侧列位置是需要计算的
            var rightLine = matrix[maxHeightLineIndex - index];
            var leftLine = matrix[maxHeightLineIndex - index - 1];
            var leftListPart;
            var rightListPart;

            matrixWidth += lineWidth + self.spaceWidth;
            rightLine.forEach(function (metaItem, metaItemIndex) {
                console.log(metaItem);
                var startX = metaItem.position.x - metaItem.rectWidth - self.spaceWidth;
                var startY = 0;

                leftListPart = leftLine.filter(function (currentLineItem) { return metaItem.previousIds.indexOf('' + currentLineItem.id) != -1;});
                if (leftListPart.length > 1) { // 从右往左看的一对多
                    startY = (leftListPart.length * (self.rectHeight + self.spaceHeight) - self.rectHeight) / 2;
                    leftListPart.forEach(function (cItem, cItemIndex) {
                        cItem.position = {
                            x: startX,
                            y: startY + cItemIndex * (self.rectHeight + self.spaceHeight)
                        };
                    });
                } else if (leftListPart.length == 1) { // 从右往左看的一对一或者多对一
                    console.log(leftListPart[0]);

                    rightListPart = rightLine.filter(function (currentLineItem) { return leftListPart[0].nextIds.indexOf('' + currentLineItem.id) != -1;});
                    var minY = rightListPart[0].position.y;
                    var maxY = rightListPart[0].position.y;
                    var height;
                    // var height = getLineHeight(rightListPart);
                    rightListPart.forEach(function (metaItem, metaItemIndex) {
                        if (metaItem.position.y > maxY) maxY = metaItem.position.y;
                        if (metaItem.position.y < minY) minY = metaItem.position.y;
                    });
                    height = maxY - minY + rightListPart[0].rectHeight;

                    if (!rightListPart[0] || !rightListPart[0].position) {
                        console.log('Error data here: [' + rightListPart[0] + ']');
                        return;
                    }

                    if (rightListPart.length == 1) {
                        leftListPart[0].position = {
                            x: startX,
                            y: rightListPart[0].position.y
                        };
                    } else {
                        leftListPart[0].position = {
                            x: startX,
                            y: rightListPart[0].position.y + (height - rightListPart[0].rectHeight) / 2
                        };
                    }
                }
            });
        });

        // 再右向计算最高列右侧的matrix的位置
        subMatrix = matrix.slice(maxHeightLineIndex + 1, matrix.length);
        subMatrix.forEach(function (line, index) {
            var lineWidth = self._getLineWidth(line);
            // 由左向右计算时，左侧的位置是已知的，右侧的位置是需要由左侧计算的
            var rightLine = line;
            var leftLine = matrix[maxHeightLineIndex + index];
            var leftListPart;
            var rightListPart;

            matrixWidth += lineWidth + self.spaceWidth;
            leftLine.forEach(function (metaItem, metaItemIndex) {
                var startX = metaItem.position.x + metaItem.rectWidth + self.spaceWidth;
                var startY = 0;
                rightListPart = rightLine.filter(function (currentLineItem) { return metaItem.nextIds.indexOf('' + currentLineItem.id) != -1;});
                if (rightListPart.length > 1) { // 从左往右看的一对多
                    startY = (rightListPart.length * (self.rectHeight + self.spaceHeight) - self.rectHeight) / 2;
                    rightListPart.forEach(function (rItem, rItemIndex) {
                        rItem.position = {
                            x: startX,
                            y: startY + rItemIndex * (self.rectHeight + self.spaceHeight)
                        };
                    });
                } else if (rightListPart.length == 1) { // 从左往右看的一对一或者多对一
                    leftListPart = leftLine.filter(function (currentLineItem) { return rightListPart[0].previousIds.indexOf('' + currentLineItem.id) != -1;});

                    var minY = leftListPart[0].position.y;
                    var maxY = leftListPart[0].position.y;
                    var height;
                    leftListPart.forEach(function (metaItem, metaItemIndex) {
                        if (metaItem.position.y > maxY) maxY = metaItem.position.y;
                        if (metaItem.position.y < minY) minY = metaItem.position.y;
                    });
                    height = maxY - minY + leftListPart[0].rectHeight;

                    if (!leftListPart[0] || !leftListPart[0].position) {
                        console.log('Error data here: [' + rightListPart[0] + ']');
                        return;
                    }

                    if (leftListPart.length == 1) { // 一对一
                        rightListPart[0].position = {
                            x: startX,
                            y: leftListPart[0].position.y
                        };
                    } else {
                        rightListPart[0].position = { // 多对一
                            x: startX,
                            y: leftListPart[0].position.y + (height - leftListPart[0].rectHeight) / 2
                        };
                    }
                }
            });
        });

        return {
            w: matrixWidth,
            h: maxLineHeight
        };
    },

    _extendRaphael: function () {
        // 扩展Raphael，加上connection函数，默认是从左向右绘制的,
        // 第一个参数表示左边的点，第二个参数表示右边的点
        Raphael.fn.connection = function (startPoint, endPoint, options) {
            if (!startPoint || !startPoint.x || !startPoint.y || !endPoint || !endPoint.x || !endPoint.y) return;

            // 向右上方指的箭头的示例："M 82 107 L 102 107 Q 112 107 112 97 L 112 27 Q 112 17 122 17 L 136 17";
            // 向右指的箭头的示例："M 141 17 L 134 21 L 136 17 L 134 14 Z";
            var pathStr = '';
            var centralX = startPoint.x + (endPoint.x - startPoint.x) / 2;
            var dy = endPoint.y > startPoint.y ? 10 : -10; // 控制箭头往上以曲线转还是往下以曲线转

            pathStr += Raphael.format("M{0},{1}", startPoint.x, startPoint.y);
            if (startPoint.y != endPoint.y) {
                pathStr += Raphael.format("L{0},{1}Q{2},{3},{4},{5}L{6},{7}Q{8},{9},{10},{11}",
                    centralX - 10, startPoint.y, centralX, startPoint.y, centralX, startPoint.y + dy,
                    centralX, endPoint.y - dy, centralX, endPoint.y, centralX + 10, endPoint.y);
            }
            pathStr += Raphael.format("L{0},{1}", endPoint.x - 5, endPoint.y);

            this.path(pathStr).attr({stroke: 'rgb(107, 77, 79)', fill: "none"});
            // 绘制箭头
            if (options && (!options.fake)) {
                this.path(Raphael.format("M{0},{1}L{2},{3}L{4},{5}L{6},{7}Z", endPoint.x, endPoint.y, endPoint.x - 7,
                    endPoint.y + 4, endPoint.x - 5, endPoint.y, endPoint.x - 7, endPoint.y - 3)
                ).attr({stroke: 'rgb(107, 77, 79)', fill: "rgb(107, 77, 79)"});
            }
        };

        Raphael.fn.button = function (x, y, w, h, text, options) {
            // rect of the button
            var box = this.rect(x, y, w, h, options.round).attr({
                fill: options.rectfill,
                'stroke': options.stroke,
                'stroke-width': options.strokeWidth
            });
            // text of the button,  left or center? default is center
            var textPosX = options.align == 'left' ? box.attrs.x + 10 : box.attrs.x + box.attrs.width / 2; // left or center
            var textPosY = options.align == 'left' ? box.attrs.y + box.attrs.height / 2 - 10 : box.attrs.y + box.attrs.height / 2;
            var textAnchor = options.align == 'left' ? 'start' : 'middle';
            var text = this.text(textPosX, textPosY, text).attr({
                "fill": options.textfill,
                "text-anchor": textAnchor,
                "font-family": options.fontFamily,
                "font-size": options.fontSize
            });

            // set of rectangle + text = button
            var button = this.set().attr({
                "fill-opacity": options.opacity,
                cursor: 'pointer'
            });
            options.rotate && box.transform('r' +  options.rotate);
            box.mouseover(options.onHoverIn).mouseout(options.onHoverOut);
            text.mouseover(options.onHoverIn);
            return button.push(box).push(text).mouseup(options.onclick);
        }
    },

    _drawMatrix: function (size) {
        var id = this.element.attr('id');
        var paper = this.paper = Raphael(id, size.w, size.h);
        var statusColorConfig = {
            'Finished': '#61b1fb',
            'Underway': '#a8d450',
            'Killed': '#d46965',
            'Failed': '#d46965',
            'Queued': '#dfdfdf'
        };
        var strokeColorConfig = {
            'Finished': '#3489d7',
            'Underway': '#75ad04',
            'Killed': '#c6150f',
            'Failed': '#c6150f',
            'Queued': '#bbbbbb'
        };
        var rectfill;
        var self = this;

        if (this.options.isBodyOnly) {
            this.matrix = this.matrix.splice(1, this.matrix.length - 2);
        }
        // 第二次绘制时需要先清空之前的内容，否则这些图会上下排列
        paper.clear();
        this.matrix.forEach(function (items, index) {
            items.forEach(function (item) {
                var rectText = item.usedTime ? item.stepName + '\n' + item.usedTime : item.stepName;
                var nextIds = item.nextIds, x, y, optionsTemp, buttonTemp;
                var rotatedWidth = item.rectWidth / 1.41;

                if (item.position) {
                    x = item.position.x;
                    y = item.position.y;
                    rectfill = statusColorConfig[item.status] || self.options.fill;
                    strokeColor = strokeColorConfig[item.status];

                    // 绘制带文字的矩形块
                    if (!item.fake) {
                        optionsTemp = {
                            rectfill: rectfill,
                            textfill: 'rgb(82,94,108)',
                            stroke: strokeColor,
                            strokeWidth: 1,
                            opacity: 0.5,
                            fontSize : self.options.fontSize,
                            onHoverIn: self._hoverCb(paper, item, true),
                            onHoverOut: self._hoverCb(paper, item, false)
                        };
                        if (item.condition && item.condition == 'start') {
                            optionsTemp.round = null;
                            optionsTemp.rotate = '45';
                            buttonTemp = paper.button(x + (item.rectWidth - rotatedWidth) / 2, y + (item.rectHeight - rotatedWidth) / 2, item.rectWidth/1.41, item.rectWidth/1.41, rectText, optionsTemp);
                            //buttonTemp.transform('r45');
                        } else {
                            optionsTemp.round = self.options.r;
                            buttonTemp = paper.button(x, y, item.rectWidth, item.rectHeight, rectText, optionsTemp);
                        }
                    } else { // 以贯穿矩形的横线横线替代
                        paper.path(Raphael.format("M{0},{1}L{2},{3}", x - 5, y + item.rectHeight / 2,
                                                  x + item.rectWidth, y + item.rectHeight / 2))
                                                  .attr({stroke: 'rgb(107, 77, 79)', fill: "none"});
                    }

                    (index + 1 < self.matrix.length) && nextIds.forEach(function (id) {
                        var nextItem;
                        self.matrix[index + 1].some(function (metaItem) { return !(nextItem = metaItem) || (metaItem.id == id); });

                        if (!nextItem || !nextItem.position) return;

                        // 绘制两个矩形的连接线
                        paper.connection({
                            x: item.position.x + item.rectWidth,
                            y: item.position.y + item.rectHeight / 2
                        },
                        {
                            x: nextItem.position.x,
                            y: nextItem.position.y + nextItem.rectHeight / 2
                        }, { fake: nextItem.fake });
                    });
                }
            });
        });
    },

    _hoverCb: function (paper, item, isIn) {
        var hoverFlag = isIn;
        var hintElementObj = {};
        var $tip = this.options.$tip;
        var self = this;

        this.hintHideTimer = null;

        return function (callback) {
            if (hoverFlag) {
                if (!item.taskId) return;

                var rectfill = "rgb(223,246,198)";
                var color = "hsb(240°, 1, 1)";
                var $dom = $(this.node);

                var x = item.position.x;
                var y = item.position.y + item.rectHeight + 5;

                var hintResetButton, hintLogButton, text = '';

                // Hack here, with this I know it's hovering in text
                if ($dom.attr('font')) { // Text的hoverIn, 只show而已
                    self.keepHint = true;
                    self.hintHideTimer && clearTimeout(self.hintHideTimer);
                    return;
                }

                // 先in再out，真是坑爹
                self.hintHideTimer && clearTimeout(self.hintHideTimer);
                if(self.options.isBodyOnly){
                    $tip.html(item.tipContent);
                } else {
                    if (!self.keepHint || self.previousTaskId != item.taskId) {
                        // HACK here, use parent to find get the height when scroll
                        // Show the loading tip before ajax response
                        if (self.previousTaskId != item.taskId) {
                            self.previousTaskId = item.taskId;
                        }

                        $tip.css('left', $(self.element).position().left + parseInt($dom.attr('x')) + 'px')
                            .css('top', $(self.element).position().top + parseInt($dom.attr('y')) + $(self.element).parent().scrollTop()
                                 + parseInt($dom.attr('height')) + 5 + 'px')
                            .show().find('.content').html('<p>Loading...</p>');

                        $.ajax({
                            url: '/mams-web/workflowprocess/process/getWorkflowStepTips.action?t=' + (new Date()).getTime(),
                            dataType: 'json',
                            method: 'post',
                            data: {taskId: item.taskId},
                            success: function(data) {
                                if (!hoverFlag) return;
                                if (data.code == 'A00000') {
                                    self._setHintContent(data.data, item.stepId, item.dynamicInstanceId, item.stepTipKey);
                                }
                            }
                        });
                    }
                }
            } else {
                self.hintHideTimer && clearTimeout(self.hintHideTimer);
                self.hintHideTimer = setTimeout(function () {
                    $tip.hide();
                    self.keepHint = false;
                }, 500);
            }
        };
    },
    _bindTipEvents: function ($tip) {
        var self = this;
        $tip.hover(function () {
            self.hintHideTimer && clearTimeout(self.hintHideTimer);
            self.keepHint = true;
        }, function () {
            self.hintHideTimer && clearTimeout(self.hintHideTimer);
            self.hintHideTimer = setTimeout(function () {
                $tip.hide();
                self.keepHint = false;
            }, 500);
        });
    },
    _setHintContent: function (taskInfo, stepId, dynamicInstanceId, stepTipKey) {
        var taskName = taskInfo.taskItemTitle || '无';
        var $tip = this.options.$tip;
        var restartButton = '';
        var moreLink = '', tableHtml = '';
        //var workflowId = this.options.workflowId;
        var workflowId = taskInfo.workflowId;//每一次hint上都会有这个步骤对应的workflowId
        var len = 30;
        var text = ['起始时间: ' + taskInfo.startTime + '<br>', '结束时间: ' + taskInfo.endTime + '<br>',
                    '进度: ' + taskInfo.progress + '%'].join('');
        var readRecordButton = stepTipKey == 'viewLog' ? '<button class="btn btn-mini btn-info" id="ReadRecord"'
                               + 'style="float: right;margin-top:10px;" data-id="' + taskInfo.taskItemId + '">查看日志</button>' : '';
        var state = $tip.find('.more-table').css('display') != 'none';
        var logUrl = taskInfo.transcodeViewLogUrl;
        var self = this;

        // 只有在有logInfo字段的时候才显示‘查看日志’按钮，以及多一行‘日志信息’
        text += (taskInfo.logInfo ? '<br>日志信息：' + taskInfo.logInfo : '');
        if (taskInfo.status == 'Underway' || taskInfo.status == 'Failed') {
            restartButton = '<button class="btn btn-mini btn-primary" id="RestartStep" style="margin-top:10px;" data-workflowId="' + workflowId + '"'
                            + 'data-stepId="' + stepId + '"' + 'data-dynamicId="' + dynamicInstanceId + '">步骤重启</button>';
        }
        if (taskInfo.taskItems && taskInfo.taskItems.length > 0) {
            taskInfo.taskItems.forEach(function (item) {
                item.startTime = self._dateFormat(item.startTime, "yyyy-MM-dd hh:mm");
                item.endTime = self._dateFormat(item.endTime, "yyyy-MM-dd hh:mm");
            });
            if (state) {
                moreLink = '<br><a href="#" style="color:#00f;" class="more" id="MoreInfo" data-state="unfold">收起<<</a><br>';
            } else {
                moreLink = '<br><a href="#" style="color:#00f;" class="more" id="MoreInfo" data-state="fold">展开>></a><br>';
            }
            tableHtml = $("#TmplTipDetails").tmpl(taskInfo.taskItems);
            $tip.find('.more-table > tbody').html(tableHtml);
        } else {
            $tip.find('.more-table').hide();
            moreLink = '<br>';
        }

        taskName = taskName.length <= len ? taskName : taskName.substring(0, len) + '..';

        $tip.find('.content').html(text + moreLink + restartButton + readRecordButton);
        if (taskInfo.taskItems && taskInfo.taskItems.length > 0) {
            $('#MoreInfo').unbind().bind('click', $.proxy(self._moreInfoCb, this));
        }
        $('#ReadRecord').unbind().bind('click', function () {
            window.open(logUrl);
        });
        if (taskInfo.status == 'Underway' || taskInfo.status == 'Failed') {
            $("#RestartStep").unbind().bind('click', $.proxy(self._restartStepCb, this));
        }
    },
    _moreInfoCb: function (e) {
        var $dom = $(e.target);
        var state = $dom.attr('data-state');
        var self = this;
        if (!state || state == 'fold') { // 初始时或折叠时,此时行为是点开展示
            $dom.html('收起<<');
            self.options.$tip.find('.more-table').show();
            $dom.attr('data-state', 'unfold');
        } else if (state == 'unfold') { // 初始时或折叠时,此时行为是点击隐藏
            $dom.html('展开>>');
            self.options.$tip.find('.more-table').hide();
            $dom.attr('data-state', 'fold');
        }
    },
    _restartStepCb: function (e) {
        var $dom = $(e.target);
        var data = {
            workflowId: $dom.attr("data-workflowId"),
            stepId: $dom.attr("data-stepId"),
            dynamicId: $dom.attr("data-dynamicId")
        };
        var self = this;

        $.ajax({
            url: '/mams-web/workflowprocess/process/restartWorkflowStep.action',
            dataType: 'json',
            method: 'post',
            data: data,
            success: function(data) {
                if (data.code == 'A00000') {
                    console.log('工作流重启成功！');
                    self.paper.clear();
                    // HACK here! use the 'refresh' button outside to refresh the chart
                    $(self.element).parent().find('[data-operation="refreshWorkflow"]').trigger('click');
                }
            }
        });
    },
    _dateFormat: function(date, format) {
        if (!date || !format) {
            return '';
        }
        if ('string' == typeof date) {
            return date;
        }
        if (!(date instanceof Date)) {
            date = new Date(date);
        }
        var o = {
            'M+': date.getMonth() + 1,
            'd+': date.getDate(),
            'h+': date.getHours(),
            'm+': date.getMinutes(),
            's+': date.getSeconds(),
            'q+': Math.floor((date.getMonth() + 3) / 3),
            'S': date.getMilliseconds()
        }

        if (/(y+)/.test(format)) {
            format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
        }

        for (var k in o) {
            if (new RegExp('(' + k + ')').test(format)) {
                format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length));
            }
        }
        return format;
    }
});
